package com.xsy.camera2.opengl

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.params.OutputConfiguration
import android.hardware.camera2.params.SessionConfiguration
import android.opengl.GLSurfaceView
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.util.Size
import android.view.Surface
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.xsy.camera2.opengl.databinding.ActivityMainBinding


class MainActivity : AppCompatActivity() {
    private lateinit var viewBinding: ActivityMainBinding
    private lateinit var mCamera: CameraDevice
    private lateinit var mCameraCaptureSession: CameraCaptureSession
    private var mHandler: Handler? = null
    private var mSurfaceTexture: SurfaceTexture? = null
    private var mRender: MyGLRender2? = null
    private lateinit var mOrientationEventListener: MyOrientationEventListener
    private var mSensorOrientation = 0
    private var mPhoneOrientation = 0
    private var mPreviewSize: Size = Size(0,0)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startOrientationChangeListener()
        enableEdgeToEdge()
        viewBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        if (mRender == null) {
            viewBinding.glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
            mRender = viewBinding.glSurfaceView.getRender()
        }
        if (mHandler == null) {
            mHandler = object : Handler(Looper.getMainLooper()) {
                override fun handleMessage(msg: Message) {
                    super.handleMessage(msg)
                    when (msg.what) {
                        1 -> {
                            val texture = msg.obj as SurfaceTexture
                            mSurfaceTexture = texture
                            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
                                videoCapture()
                            }
                        }

                        else -> {
                            Log.e(TAG, "others message: ${msg.what}")
                        }
                    }
                }
            }
        }
        if (allPermissionGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(this, REQUEST_PERMISSIONS, REQUEST_CODE_PERMISSION)
        }
    }

    @RequiresApi(Build.VERSION_CODES.P)
    private fun videoCapture() {
        if (mSurfaceTexture == null) return

        mSurfaceTexture?.setDefaultBufferSize(1280, 720)
        val surface = Surface(mSurfaceTexture)

        val requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
        requestBuilder.addTarget(surface)
        val configuration = OutputConfiguration(surface)
        val outputs = ArrayList<OutputConfiguration>()
        outputs.add(configuration)
        val config = SessionConfiguration(
            SessionConfiguration.SESSION_REGULAR,
            outputs,
            ContextCompat.getMainExecutor(this),
            object : CameraCaptureSession.StateCallback() {
                override fun onConfigured(session: CameraCaptureSession) {
                    mCameraCaptureSession = session
                    session.setRepeatingRequest(requestBuilder.build(), null, null)
                    Log.e("MainActivity", "onConfigured")
                }

                override fun onConfigureFailed(session: CameraCaptureSession) {
                }

            })
        mCamera.createCaptureSession(config)

    }

    override fun onPause() {
        super.onPause()
        mRender?.setHandler(null)
        viewBinding.glSurfaceView.onPause()
        mCameraCaptureSession?.close()
        mCamera?.close()
    }


    override fun onDestroy() {
        super.onDestroy()
        viewBinding.glSurfaceView.onDestroy()
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSION) {
            if (allPermissionGranted()) {
                startCamera()
            } else {
                Toast.makeText(this, "Permission not granted by the user", Toast.LENGTH_SHORT)
                    .show()
            }
        }
    }


    private fun allPermissionGranted() = REQUEST_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
    }


    private fun startCamera() {
        val manager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
        val cameraId = manager.cameraIdList[0]
        val characteristics = manager.getCameraCharacteristics(cameraId)
        //相机的角度
        mSensorOrientation = characteristics[CameraCharacteristics.SENSOR_ORIENTATION]!!
        Log.e("startCamera:", "$mSensorOrientation")
        calculatePreviewRotation()

        val map = characteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]!!
        val outputSizes = map.getOutputSizes(SurfaceTexture::class.java)
        mPreviewSize = chooseBestSize(outputSizes, 1280, 720)
        try {
            manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
                override fun onOpened(camera: CameraDevice) {
                    mCamera = camera
                    mRender?.setHandler(mHandler)
                    mRender?.setVideoSize(mPreviewSize)
                }

                override fun onDisconnected(camera: CameraDevice) {
                }

                override fun onError(camera: CameraDevice, error: Int) {
                }

            }, null)
        } catch (se: SecurityException) {
            Log.e(TAG, "exception: ${se.message}")
        }

    }

    private fun chooseBestSize(sizes: Array<Size>, width: Int, height: Int): Size {
        var bestSize: Size = sizes[0]
        var minDiffWidth = Int.MAX_VALUE
        var minDiffHeight = Int.MAX_VALUE
        val targetWidth = width
        val targetHeight = height
        for (size in sizes) {
            val diffWidth = Math.abs(size.width - targetWidth)
            val diffHeight = Math.abs(size.height - targetHeight)

            val isAspectRatioValid =
                Math.abs(size.width.toFloat() / size.height - 4.0f / 1.0f) < 0.01f ||
                        Math.abs(size.width.toFloat() / size.height - 16.0f / 9.0f) < 0.01f
            if (isAspectRatioValid && (diffWidth < minDiffWidth || (diffWidth == minDiffWidth && diffHeight < minDiffHeight))) {
                bestSize = size
                minDiffWidth = diffWidth
                minDiffHeight = diffHeight
            }
        }
        return bestSize
    }


    fun getRotation(): Int {
        val display = (getSystemService(WINDOW_SERVICE) as WindowManager).defaultDisplay
        val screenRotation = display.rotation

        when(screenRotation){
            Surface.ROTATION_0 -> {
                return 0
            }
            Surface.ROTATION_90 -> {
                return 90
            }
            Surface.ROTATION_180 -> {
                return 180
            }
            Surface.ROTATION_270 -> {
                return 270
            }
            else -> {
                return 0
            }
        }
    }

    fun setPhoneOrietation(orientation: Int){
        this.mPhoneOrientation = orientation
    }

    fun startOrientationChangeListener() {
        mOrientationEventListener = MyOrientationEventListener(this)
        mOrientationEventListener.setActivity(this)
        mOrientationEventListener.enable()
    }

    fun calculatePreviewRotation(){
        val result = ( mSensorOrientation - mPhoneOrientation * -1 + 360 ) % 360
//        Log.e("calculatePreviewRotation:", "$result")
        mRender?.setOrientation(result)
    }

    companion object {
        private const val TAG = "CameraxDemo"
        private const val REQUEST_CODE_PERMISSION = 0x1
        private val REQUEST_PERMISSIONS = mutableListOf(
            Manifest.permission.CAMERA,
            Manifest.permission.RECORD_AUDIO
        ).apply {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
                add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            }
        }.toTypedArray()
    }
}